package com.liwy3.thread;

import lombok.extern.slf4j.Slf4j;
import sun.misc.Unsafe;

import java.lang.reflect.Field;

/**
 * Copyright © 2022年10月21日 北京自如信息科技有限公司. All rights reserved.
 *
 * @Prject: liwy3
 * @Package: com.liwy3.thread
 * @Description: cas底层原理 ABA问题解决： AtomicStampedReference类似于乐观锁添加版本号
 * @author: Crazy4J
 * @date: 2022年10月21日 16:27
 * @version: V1.0
 */
@Slf4j
public class CasDemo {

    public int count = 0;
    private static Unsafe unsafe;
    private long index;

    static {
        unsafe = getUnsafe();
    }

    {
        assert unsafe != null;
        Field field = null;
        try {
            field = CasDemo.class.getDeclaredField("count");
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
        index = unsafe.objectFieldOffset(field);
    }


    public static void main(String[] args) {

        CasDemo casDemo = new CasDemo();
        casDemo.testAdd();
    }

    public void testAdd() {

        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
//                add();
                casAdd();
            }).start();
        }
        try {
            Thread.sleep(1000 * 3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(count);

    }

    public void add() {
        for (int i = 0; i < 100; i++) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            count++;
        }
    }

    public void casAdd() {
        CasDemo casDemo = new CasDemo();
        for (int i = 0; i < 100; i++) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            int currentCount;
            do {
                //this对象为持有count的对象实例
                currentCount = unsafe.getInt(this, index);
            } while (!unsafe.compareAndSwapInt(this, index, currentCount, currentCount + 1));
        }
    }

    private static Unsafe getUnsafe() {
        try {
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            return (Unsafe) field.get(null);
        } catch (Exception e) {
            log.error(e.getMessage());
            return null;
        }
    }

}
